package com.shadowvc.sdk.internal.convert;

import com.shadowvc.sdk.Constants;
import com.shadowvc.sdk.exception.ApiException;
import com.shadowvc.sdk.internal.annotation.ApiField;
import com.shadowvc.sdk.internal.annotation.ApiListField;
import com.shadowvc.sdk.internal.json.JSONWriter;
import com.shadowvc.sdk.internal.util.ApiEnum;
import com.shadowvc.sdk.internal.util.ApiObject;
import com.shadowvc.sdk.internal.util.ObjectUtil;
import com.shadowvc.sdk.internal.util.StringUtil;

import java.lang.reflect.Field;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * Created by chenxiaochun on 16/4/7.
 */
public class ToJsonConverter {

  /**
   * 转换成JSON
   *
   * @param object
   * @return
   */
  public String toJson(Object object) {
    DateFormat df = new SimpleDateFormat(Constants.DATE_TIME_FORMAT);
    df.setTimeZone(TimeZone.getTimeZone(Constants.DATE_TIMEZONE));
    return new JSONWriter(df).write(object);
  }

  /**
   * 列表转json
   *
   * @param list  列表
   * @param clazz 对象类型
   * @return 返回
   */
  public String toString(List<?> list, Class<?> clazz) throws ApiException {
    //如果是业务对象
    if (ApiObject.class.isAssignableFrom(clazz)) {
      StringBuilder sb = new StringBuilder();
      sb.append("[");
      int i = 0;
      for (Object obj : list) {
        if (i == 1) sb.append(",");
        sb.append(toString(obj));
        i = 1;
      }
      sb.append("]");
      return sb.toString();
    }

    //如果是枚举
    if (ApiEnum.class.isAssignableFrom(clazz)) {
      StringBuilder sb = new StringBuilder();
      sb.append("[");
      int i = 0;
      for (Object obj : list) {
        if (i == 1) sb.append(",");
        sb.append("\"" + obj + "\"");
        i = 1;
      }
      sb.append("]");
      return sb.toString();
    }

    //原生类型
    return toJson(list);
  }

  /**
   * 对象转json
   *
   * @param object 对象
   * @return 返回
   */
  public String toString(Object object) throws ApiException {
    Map<String, Object> resultMap = new TreeMap<>();
    toSuccessMap(object, resultMap);
    return toJson(resultMap);
  }

  /**
   * 处理map
   *
   * @param in  输入
   * @param out 输出
   */
  private void toMap(Map<?, ?> in, Map<String, Object> out) throws ApiException {
    for (Object key : in.keySet()) {
      Object value = in.get(key);
      Map<String, Object> map = new TreeMap<>();
      toSuccessMap(value, map);
      out.put((String) key, map);
    }
  }

  /**
   * 转map
   *
   * @param object 对象
   * @param map    map
   */
  private void toSuccessMap(Object object, Map<String, Object> map) throws ApiException {
    if (Map.class.isAssignableFrom(object.getClass())) {//如果对象本身就是map
      toMap((Map<?, ?>) object, map);
    } else {
      Class<?> clazz = object.getClass();
      Field[] fields = clazz.getDeclaredFields();// 不包括父类
      for (int i = 0; i < fields.length; i++) {
        fields[i].setAccessible(true); // 使属性可以只接访问

        //apiListField
        processApiListField(object, map, fields[i]);

        //apiField
        processApiField(object, map, fields[i]);
      }
    }
  }

  /**
   * 处理apiField
   *
   * @param object
   * @param map
   * @param field
   * @throws ApiException
   */
  private void processApiField(Object object, Map<String, Object> map, Field field) throws ApiException {
    ApiField apiField = field.getAnnotation(ApiField.class);

    // 如果没有ApiField就不是api字段
    if (apiField == null) {
      return;
    }

    String nodeName = apiField.value();
    if (StringUtil.isEmpty(nodeName)) {
      nodeName = field.getName();
    }

    // 获取值，如果值为空那么跳过
    Object value;
    try {
      value = field.get(object);
    } catch (IllegalAccessException e) {
      throw new ApiException(201, nodeName + "无法读取属性值");
    }
    if (value == null) {// 让null也传输
      map.put(nodeName, null);
      return;
    }

    // 给字段赋值
    Class<?> clazz = value.getClass();
    if (String.class.isAssignableFrom(clazz)) {
      map.put(nodeName, value);
    } else if (Long.class.isAssignableFrom(clazz)) {
      map.put(nodeName, value);
    } else if (Integer.class.isAssignableFrom(clazz)) {
      map.put(nodeName, value);
    } else if (Boolean.class.isAssignableFrom(clazz)) {
      map.put(nodeName, value);
    } else if (Double.class.isAssignableFrom(clazz)) {
      map.put(nodeName, value);
    } else if (Number.class.isAssignableFrom(clazz)) {
      map.put(nodeName, value);
    } else if (Date.class.isAssignableFrom(clazz)) {
      DateFormat df = new SimpleDateFormat(Constants.DATE_TIME_FORMAT);
      df.setTimeZone(TimeZone.getTimeZone(Constants.DATE_TIMEZONE));
      map.put(nodeName, df.format((Date) value));
    } else if (clazz.isEnum()) {// 枚举
      map.put(nodeName, value);
    } else {
      // 如果是一个对象，那么必须再深入获取
      Map<String, Object> _map = new TreeMap<>();
      map.put(nodeName, _map);
      toSuccessMap(value, _map);
    }
  }

  /**
   * 处理apiListField
   *
   * @param object
   * @param map
   * @param field
   * @throws ApiException
   */
  private void processApiListField(Object object, Map<String, Object> map, Field field) throws ApiException {
    ApiListField apiListField = field.getAnnotation(ApiListField.class);
    if (apiListField != null) {
      String nodeName = apiListField.value();
      if (StringUtil.isEmpty(nodeName)) {
        nodeName = field.getName();
      }

      // 获取值，如果值为空那么跳过
      Object value;
      try {
        value = field.get(object);
      } catch (IllegalAccessException e) {
        throw new ApiException(201, nodeName + "无法读取属性值");
      }
      if (value == null || ((List<?>) value).size() == 0) {
        return;
      }

      //是eop对象才继续深入
      Class<?> clazz = ObjectUtil.getParameterizedType(field, 0);
      int size = ((List<?>) value).size();
      if (ApiObject.class.isAssignableFrom(clazz)) {
        Set<Map<String, Object>> _set = new LinkedHashSet<>();
        for (int j = 0; j < size; j++) {
          Map<String, Object> _map = new TreeMap<>();
          _set.add(_map);
          toSuccessMap(((List<?>) value).get(j), _map);
        }
        map.put(nodeName, _set);
      } else {
        map.put(nodeName, value);
      }
    }
  }


}
